use aruspider::{Method, ParseResult, Request, RequestBody, Response, Spider, Task};
use async_trait::async_trait;
use log::debug;
use serde::Serialize;
use std::sync::Arc;
use visdom::Vis;

#[derive(Debug, Clone, Serialize)]
pub struct Movie {
    pub title: String,
    pub tags: String,
    pub publish: String,
    pub score: f32,
    pub desc: String,
}

#[derive(Debug)]
pub struct MovieSpider {
    pub start_urls: Vec<&'static str>,
}

#[async_trait]
impl Spider for MovieSpider {
    type ItemType = Movie;
    type M = Movie;

    fn start_urls(&self) -> &[&'static str] {
        self.start_urls.as_slice()
    }

    // 默认的 start_requests 即可

    // 重写 parse
    async fn parse(
        &self,
        resp: Response,
        _meta: Option<Self::M>,
    ) -> ParseResult<Self::ItemType, Self::M> {
        let mut tasks = vec![];
        let items = vec![];

        // - 解析列表页
        // -- metadata + 电影详情 url
        let Response { resp, from_url } = resp;
        debug!("解析列表页 {}", from_url);
        let page = resp.text().await.unwrap();
        // debug!("--> {:?}", &page[..10]);
        let page = Vis::load(&page).expect(&format!("解析 {} 页面错误", from_url.to_string()));

        let new_tasks = page.find("div.el-card__body").map(|_, ele| {
            let ele = Vis::dom(ele);
            let title = ele.find("a.name > h2").text().to_string();
            let tags = ele
                .find("div.categories>button.category>span")
                .map(|_, ele| ele.inner_html().to_string());
            let publish = ele.find("div.m-v-sm").eq(1).find("span").text().to_string();
            let score = ele.find("p.score").text().to_string();
            let meta = Movie {
                title,
                tags: tags.join(","),
                publish,
                score: score.parse::<f32>().unwrap_or_default(),
                desc: String::new(),
            };

            let detail_url = ele.find("a.name").attr("href").unwrap().to_string();
            let detail_url = from_url.clone().join(&detail_url).unwrap();

            // new Request
            let new_req = Request::new(detail_url, Method::GET, None, None, RequestBody::Empty);
            Task {
                req: new_req,
                meta: Some(meta),
                parser: Some(Arc::new(parse_detail)),
            }
        });

        tasks.extend(new_tasks);

        // -- next page url
        match page.find("a.next").attr("href") {
            Some(next_url) => {
                let next_url = from_url.join(&next_url.to_string()).unwrap();
                debug!("找到下一页 {}, 继续爬取下一页", next_url);
                let next_req = Request::new(next_url, Method::GET, None, None, RequestBody::Empty);
                let next_task = Task {
                    req: next_req,
                    meta: None,
                    parser: None,
                };
                tasks.push(next_task);
            }
            None => {}
        }

        ParseResult::new(tasks, items)
    }
}

async fn parse_detail(resp: Response, metadata: Option<Movie>) -> ParseResult<Movie, Movie> {
    let reqs = vec![];
    let mut items = vec![];

    debug!("解析详情页...");
    let Response { resp, .. } = resp;

    let mut movie = metadata.unwrap();

    let page = resp.text().await.unwrap();
    let page = Vis::load(&page).unwrap();
    let desc = page.find("div.drama > p").text().to_string();
    movie.desc = desc;
    debug!("{}", movie.desc);
    // pipeline 影响了异步执行....
    // 下次试试 rbatis 的 pipeline 会不会影响.
    // items.push(movie);

    ParseResult::new(reqs, items)
}
